home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 19 / CU Amiga Magazine's Super CD-ROM 19 (1998)(EMAP Images)(GB)[!][issue 1998-02].iso / CUCD / Online / NNTPd / server / msgidd.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-09  |  11.3 KB  |  551 lines

  1. /* msgidd -- message ID daemon
  2.  * vix 24may90 [written]
  3.  *
  4.  * with mods ken@sdd.hp.com 01jul90
  5.  * speedups by Geoff Collyer, 26 July 1992
  6.  *
  7.  * $Id: msgidd.c,v 1.7 1994/12/09 02:52:18 sob Exp sob $
  8.  */
  9.  
  10. #include "common.h"
  11. #include <signal.h>
  12. #include <sys/socket.h>
  13. #include <sys/un.h>
  14. #include <sys/time.h>
  15. #define NEEDMSGS
  16. #include "msgid.h"
  17.  
  18. #define ASSERT(e, m) if (!(e)) {fputs("assert failed... ", stderr);\
  19.                 perror(m); exit(1);}
  20.  
  21. #define STREQ(s1, s2)        (*(s1) == *(s2) && strcmp(s1, s2) == 0)
  22. #define STRN_EQ(s1, s2, n)    (*(s1) == *(s2) && strncmp(s1, s2, n) == 0)
  23.  
  24. #define FLAGS_RESETLOG    0x02
  25. #define FLAGS_FLUSHLOG    0x04
  26. #define MAX_AGE        10
  27. #define ALARM_TIME    300
  28.  
  29. #define HASHSIZE 1024
  30.  
  31. #if 0
  32. #define dprintf fprintf
  33. #else
  34. #define dprintf (void)
  35. #endif
  36.  
  37. char *malloc();
  38. extern int errno;
  39.  
  40. int log = 0, flags = 0;
  41. time_t hold_time = MAX_AGE * 60;
  42. char *hosts[100], *lfn, *ptime();
  43. FILE *logfp = NULL;
  44.  
  45. struct {
  46.     int connected;
  47.     int connects, drops;
  48.     int new, dup, cancel;
  49.     int freed;
  50. } stats;
  51.  
  52. struct el {
  53.     struct el *next;
  54.     time_t age;
  55.     int refcnt;
  56.     char id[1];
  57. } *ids[HASHSIZE];
  58.  
  59. char *months[12] = {
  60.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  61.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  62. };
  63.  
  64. static int transaction();
  65.  
  66. static unsigned
  67. mkhash(s)
  68. register unsigned char *s;
  69. {
  70.     register unsigned hash = 0;
  71.     register unsigned char c;
  72.  
  73.     while ((c = *s++) != '\0')
  74.         hash += c;
  75.     return hash;
  76. }
  77.  
  78. #ifndef BSD_44
  79. static char *
  80. strdup(x)
  81. char *x;
  82. {
  83.     register char *y = malloc(strlen(x) + 1);
  84.  
  85.     if (y)
  86.         strcpy(y, x);
  87.     return y;
  88. }
  89. #endif
  90.  
  91. static void
  92. savepid ()
  93. {
  94.   FILE *pidfp ;
  95.  
  96.   if ((pidfp = fopen (PIDFILE,"w")) == NULL)
  97.     return ;
  98.  
  99.   (void) fprintf (pidfp,"%d\n",getpid()) ;
  100.   (void) fclose (pidfp) ;
  101. }
  102.  
  103. char *
  104. ptime(now)
  105.     time_t now;
  106. {
  107.     static char buf[50];
  108.     struct tm *tmp;
  109.  
  110.     tmp = localtime(&now);
  111.     (void) sprintf(buf, "%s %2d %02d:%02d:%02d", 
  112.            months[tmp->tm_mon], tmp->tm_mday, tmp->tm_hour, 
  113.            tmp->tm_min, tmp->tm_sec);
  114.     return (buf);
  115. }
  116.  
  117. static void 
  118. usage(me) 
  119.     char *me;
  120. {
  121.     (void) fprintf(stderr, "Usage: %s [options]\n", me);
  122.     (void) fprintf(stderr, "Options: -s <unix domain socketname> [%s]\n",
  123.            SOCKNAME);
  124.     (void) fprintf(stderr, "         -l <log file name>\n");
  125.     (void) fprintf(stderr, "         -h <hold time in minutes>\n");
  126.     exit(1);
  127. }
  128.  
  129. static void
  130. openlogfile()
  131. {
  132.     if (logfp)
  133.     (void) fclose(logfp);
  134.     if (log && (logfp = fopen(lfn, "a+")) == NULL) {
  135.     syslog(LOG_ERR, "Unable to open %s: %m", lfn);
  136.     log = 0;
  137.     logfp = NULL;
  138.     }
  139. }
  140.  
  141. static SIGRET
  142. bye()
  143. {
  144.     if (log)
  145.     (void) fclose(logfp);
  146.     dprintf(stderr,"Bye !!\n");
  147.     exit(0);
  148. }
  149.  
  150. static SIGRET
  151. resetlog() {
  152.     flags |= FLAGS_RESETLOG;
  153.     signal(SIGHUP, resetlog);
  154. }
  155.  
  156. static SIGRET
  157. pstats() 
  158. {
  159.     char msgbuf[1024];
  160.  
  161.     if (log)
  162.     flags |= FLAGS_FLUSHLOG;
  163.     sprintf(msgbuf, "stats: %d connected, %d connects, %d drops, %d dups, %d new, %d cancel, %d freed\n",
  164.         stats.connected, stats.connects, stats.drops, stats.dup, stats.new,
  165.         stats.cancel, stats.freed);
  166.     dprintf(stderr, "%s\n", msgbuf);
  167.     syslog(LOG_INFO, msgbuf);
  168.     stats.connects = stats.drops = stats.new = stats.cancel = stats.dup =
  169.     stats.freed = 0;
  170.     signal(SIGALRM, pstats);
  171.     alarm(ALARM_TIME);
  172. }
  173.  
  174. SIGRET onpipe();
  175.  
  176. main(argc, argv)
  177.     int argc;
  178.     char *argv[];
  179. {
  180.     register char *sn = SOCKNAME;
  181.     register int s;
  182.     int highest_fd;
  183.     struct sockaddr_un n, in;
  184.     fd_set clients;
  185.     extern char *optarg;
  186.     extern int optind;
  187.  
  188.     while ((s = getopt(argc, argv, "l:h:s:")) != EOF)
  189.     switch(s) {
  190.         case 'h':
  191.         hold_time = 60 * atoi(optarg);
  192.         if (hold_time <= 0 || hold_time > (24 * 3600))
  193.             usage(argv[0]);    
  194.         break;
  195.         case 's':
  196.         sn = strdup(optarg);
  197.         break;
  198.         case 'l':
  199.         log++;
  200.         lfn = strdup(optarg);
  201.         break;
  202.         default:
  203.         usage(argv[0]);    
  204.         break;
  205.     }
  206.  
  207.     if (optind != argc)
  208.     usage(argv[0]);
  209.     
  210.     if (log) {
  211.     openlogfile();
  212.     if (!log) {
  213.         (void) fprintf(stderr, "%s: Unable to open log file (%s)\n", 
  214.                argv[0], lfn);
  215.         exit(1);
  216.     }
  217.     }
  218.  
  219.     savepid () ;
  220.  
  221. #ifdef SYSLOG
  222. # ifdef LOG_DAEMON
  223.     openlog("msgidd", LOG_PID, SYSLOG);
  224. # else
  225.     openlog("msgidd", LOG_PID);
  226. # endif
  227. #endif
  228.  
  229.     s = socket(PF_UNIX, SOCK_STREAM, 0);
  230.     ASSERT(s>=0, "socket");
  231.     highest_fd = s;
  232.  
  233.     n.sun_family = AF_UNIX;
  234.     (void) strcpy(n.sun_path, sn);
  235.  
  236.     (void) unlink(sn);
  237.     ASSERT(0<=bind(s, (struct sockaddr*)&n, 
  238.     strlen(n.sun_path) +sizeof n.sun_family),n.sun_path);
  239.  
  240.     FD_ZERO(&clients);
  241.     listen(s, 5);
  242.  
  243.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  244.     signal(SIGHUP, resetlog);
  245.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  246.     signal(SIGINT, bye);
  247.     signal(SIGUSR1, bye);        /* for profiling, etc. */
  248.     signal(SIGALRM, pstats);
  249.     signal(SIGPIPE, onpipe);
  250.     alarm(ALARM_TIME);
  251.  
  252.     for (;;) {
  253.     register int nfound, fd;
  254.     fd_set readfds;
  255.  
  256.     if (flags) {
  257.         if (flags & FLAGS_FLUSHLOG)
  258.         (void) fflush(logfp);
  259.         if (flags & FLAGS_RESETLOG)
  260.         openlogfile();
  261.         flags = 0;
  262.     }
  263.     readfds = clients;    /* we want to select the clients... */
  264.     FD_SET(s, &readfds);    /* ...and the main server socket. */
  265.     nfound = select(highest_fd+1, &readfds, NULL, NULL, NULL);
  266.     if (nfound < 0 && errno == EINTR)
  267.         continue;
  268.     ASSERT(0<=nfound, "select");
  269.     for (fd = 0; fd <= highest_fd; fd++) {
  270.         if (FD_ISSET(fd, &readfds)) {
  271.         if (fd == s) {
  272.             int fromlen = sizeof(in);
  273.  
  274.             if ((fd = accept(s, (struct sockaddr *) &in, 
  275.                      &fromlen)) == -1) {
  276.             syslog(LOG_ERR, "Accept failed: %m");
  277.             } else {
  278.             FD_SET(fd, &clients);
  279.             if (fd > highest_fd)
  280.                 highest_fd = fd;
  281.             stats.connects++;
  282.             stats.connected++;
  283.             }
  284.         } else if (FD_ISSET(fd, &clients)) {
  285.             if (-1 == transaction(fd)) {
  286.             close(fd);
  287.             FD_CLR(fd, &clients);
  288.             stats.connected--;
  289.             stats.drops++;
  290.             if (hosts[fd]) {
  291.                 if (log) {
  292.                 (void) fprintf(logfp, "%s Disconnect %s\n", 
  293.                     ptime(time((time_t *)0)), hosts[fd]);
  294.                 }
  295.                 dprintf(stderr, "Disconnect(%d/%s)\n",
  296.                 fd, hosts[fd]);
  297.                 free(hosts[fd]);
  298.                 hosts[fd] = NULL;
  299.                 }
  300.             }
  301.         } else {
  302.             dprintf(stderr, "Bad fd %d from select\n", fd);
  303.         }
  304.         }
  305.     }
  306.     }
  307. }
  308.  
  309. int sigpiped;
  310.  
  311. static int
  312. reply(fd, ans)
  313.     register int fd, ans;
  314. {
  315.     int status;
  316.  
  317.     sigpiped = 0;
  318.     while ((status = write(fd, (ans ? "\001" : "\000"), 1)) < 0
  319.     && errno == EINTR && !sigpiped)
  320.     ;
  321.     if (status < 0) {
  322.     if (log) {
  323.         register time_t now = time((long *)0);
  324.  
  325.         fprintf(logfp, "%s write failed (fd %d)\n", ptime(now), fd);
  326.     }
  327.     syslog(LOG_ERR, "write failed, closing connection: %m");
  328.     return -1;
  329.     }
  330.     return (0);
  331. }
  332.  
  333. static void
  334. cancel(fd, bufp, now)
  335.     register int fd;
  336.     register char *bufp;
  337.     time_t now;
  338. {
  339.     register struct el *i, *p;
  340.     register int found = 0, hash = mkhash(bufp) % HASHSIZE;
  341.  
  342.     for (i = ids[hash], p = NULL; i; p = i, i = i->next)
  343.     if (STREQ(i->id, bufp)) {
  344.         if (p == NULL)
  345.         ids[hash] = i->next;
  346.         else
  347.         p->next = i->next;
  348.         if (log)
  349.         (void) fprintf(logfp, "%s Cancel %s %s\n", 
  350.                    ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
  351.         free(i);
  352.         found++;
  353.         stats.cancel++;
  354.         dprintf(stderr, "Cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  355.         break;
  356.     }
  357.     if (!found) {
  358.     dprintf(stderr, "Bad-cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  359.     syslog(LOG_ERR, "Cancel, %s not found", bufp);
  360.     if (log)
  361.         (void) fprintf(logfp, "%s Error cancel %s %s\n", 
  362.                ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
  363.     }
  364. }
  365.  
  366. static void
  367. add(fd, bufp, n, now)
  368.     register int fd;
  369.     register char *bufp;
  370.     register int n;
  371.     time_t now;
  372. {
  373.     register struct el *i;
  374.     register int hash = mkhash(bufp) % HASHSIZE;
  375.  
  376.     /* this malloc includes the id[1] array, which means
  377.      * that there's already room for strcpy's null
  378.      */
  379.     i = (struct el *) malloc(sizeof(struct el) + n);
  380.     if (i == NULL)
  381.     return;
  382.     i->age = now;
  383.     (void) strcpy(i->id, bufp);
  384.     if (log) {
  385.     i->refcnt = 1;
  386.     (void) fprintf(logfp, "%s Add %s %s\n", ptime(now),
  387.                 hosts[fd] ? hosts[fd] : "", bufp);
  388.     }
  389.     i->next = ids[hash];
  390.     ids[hash] = i;
  391.     stats.new++;
  392.     dprintf(stderr, "Add(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  393. }
  394.  
  395. static int
  396. search(fd, bufp, now)
  397.     register int fd;
  398.     char *bufp;            /* no leading '<' */
  399.     time_t now;
  400. {
  401.     register struct el *i, *p;
  402.     register char bufc = *bufp;
  403.     register int hash = mkhash(bufp) % HASHSIZE;
  404.     int found = 0, searched = 0;
  405.  
  406.     /* 
  407.      * search the appropriate list.
  408.      */
  409.     for (i = ids[hash], p = NULL; i; p = i, i = i->next) {
  410.     /*
  411.      * if too old, everything from here to the end
  412.      * can be nuked (we always add at the top).
  413.      */
  414.     if ((now - i->age) > hold_time) {
  415.         while (i) {
  416.         register struct el *n = i->next;
  417.  
  418.         if (p)
  419.             p->next = n;
  420.         else
  421.             ids[hash] = n;
  422.         free((char *)i);
  423.         i = n;
  424.         stats.freed++;
  425.         }
  426.         break;
  427.     }
  428.  
  429.     searched++;
  430.  
  431.     if (STREQ(i->id, bufp)) {
  432.         if (log) {
  433.         i->refcnt++;
  434.         (void) fprintf(logfp, "%s Lose %s %d %ld %s\n", 
  435.                ptime(now), hosts[fd] ? hosts[fd] : "", i->refcnt, 
  436.                    (now - i->age), i->id);
  437.         }
  438.         found++;
  439.         stats.dup++;
  440.         break;
  441.     }
  442.     }
  443.  
  444.     dprintf(stderr, "Search(%d/%s): %s(%d) `%s'\n",
  445.         fd, hosts[fd], (found ? "dup" : "new"), searched, bufp);
  446.     return (found);
  447. }
  448.  
  449. /* returns: -1 == client is gone, close this fd please
  450.  *           0 == success
  451.  */
  452. static int
  453. transaction(fd)
  454.     register fd;
  455. {
  456.     char buf[1023];
  457.     register int n;
  458.     register char *bufp, *cmdp;
  459.     register time_t now = time((long *)0);
  460.  
  461.     /* read the request.  zero-length read means connection is gone.
  462.      */
  463.     do {
  464.     n = read(fd, buf, sizeof(buf));
  465.     if (n == 0)
  466.         return -1;
  467.     } while (n < 0 && errno == EINTR);
  468.     ASSERT(n>0, "read");
  469.  
  470.     if (hosts[fd]) {
  471.     dprintf(stderr, "Parse(%d/%s): `%s'\n", fd, hosts[fd], buf);
  472.     }
  473.  
  474.     /* Separate cmd from id 
  475.      */
  476.     cmdp = buf;
  477.     bufp = buf + 4;
  478.     n -= 4;
  479.  
  480.     /* find the first useful character, saving it and its address.
  481.      */
  482.     if (*bufp == '<') {
  483.     bufp++;
  484.     n--;
  485.     }
  486.  
  487.     /* rip out useless characters at end, remembering real length.
  488.      */
  489.     while (n > 0) {
  490.     register x = n - 1;
  491.     register ch = bufp[x];
  492.  
  493.     if (ch == '\n' || ch == '\r' || ch == '>')
  494.         n = x;
  495.     else
  496.         break;
  497.     }
  498.     bufp[n] = '\0';
  499.  
  500.     /* 
  501.      * Which cmd ?
  502.      */
  503.     if (STRN_EQ(cmdp, msgs[MCANCEL], 4)) {
  504.     cancel(fd, bufp, now);
  505.     return reply(fd, 0);
  506.     } else if (STRN_EQ(cmdp, msgs[MADD], 4)) {
  507.     if (search(fd, bufp, now))
  508.         return reply(fd, 1);
  509.     else { 
  510.         add(fd, bufp, n, now);
  511.         return reply(fd, 0);
  512.     }
  513.     } else if (STRN_EQ(cmdp, msgs[MOLD], 4)) {
  514.     if (log)
  515.         (void) fprintf(logfp, "%s Old %s %s\n", ptime(now), 
  516.                hosts[fd] ? hosts[fd] : "", bufp);
  517.     dprintf(stderr, "Old(%d/%s): `%s'\n", fd, hosts[fd], bufp);
  518.     return reply(fd, 0);
  519.     } else if (STRN_EQ(cmdp, msgs[MHOST], 4)) {
  520.     if (hosts[fd])
  521.         free(hosts[fd]);
  522.     hosts[fd] = strdup(bufp);
  523.     if (log)
  524.         (void) fprintf(logfp, "%s Connect %s\n", ptime(now), hosts[fd]);
  525.     dprintf(stderr, "Connect(%d/%s)\n", fd, hosts[fd]);
  526.     return reply(fd, 0);
  527.     } else {
  528.     syslog(LOG_ERR, "Unknown command %s", cmdp);
  529.     if (log)
  530.         (void) fprintf(logfp, "%s Error %s unknown-cmd %s\n", ptime(now), 
  531.                hosts[fd], cmdp);
  532.     dprintf(stderr, "Error(%d/%s) unknown-cmd %s\n", fd, hosts[fd], cmdp);
  533.     return reply(fd, 0);
  534.     }
  535. #ifdef lint
  536.     /*NOTREACHED*/
  537.     return (0);
  538. #endif
  539. }
  540.  
  541. static SIGRET onpipe()
  542. {
  543.     register time_t now = time((long *)0);
  544.  
  545.     if (log)
  546.         fprintf(logfp, "%s Got SIGPIPE\n", ptime(now));
  547.     sigpiped++;
  548.     signal(SIGPIPE, onpipe);
  549. }
  550.  
  551.